# -*-coding:utf-8-*-

from bson.objectid import ObjectId
from daos.base_async import BaseDao, BaseRecord
import time, pymongo, json, logging, datetime
from utils.misc import generate_random_no
from tornado.gen import coroutine, Return
from tornado.ioloop import PeriodicCallback
from tornado.options import options

Orders_Document_Structure = {
    '_id': 'ORDER_ID',
    'uid': 'USER_ID',  # user_id, 消费者

    'created_time': 123,  # 订单创建时间
    'paid_time': 123,
    'done_time': 123,
    'confirm_by_sys': True,  # False mean confirm by user
    'delivered_time': 123,
    'comment': 'user comment of this order',

    'return_reason': 'xxx',
    'refuse_reason': 'yyy',

    # 发货
    'deliver_remark': 'foo',
    'deliver_express': 'STO | ZTO | ...',
    'deliver_express_no': '12345...',
    'deliver_address': {
        'consignee': '张三',  # 收货人
        'phone': '13000000000',  # 收货人联系电话
        'area': '广东省 珠海市 香洲区',
        'detail': '详细的街道地址',
        'postal_code': '519000',  # 邮政编码
    },
    # 退货
    'returned_remark': '退货完成备注信息',
    'return_remark': 'bar',
    'return_express': 'STO | ZTO | ...',
    'return_express_no': '54321...',
    'return_address': {
        'consignee': 'Li Si',  # 收货人
        'phone': '13000000001',  # 收货人联系电话
        'area': '广东省 珠海市 香洲区',
        'detail': '详细的街道地址',
        'postal_code': '519000',  # 邮政编码
    },
    # 订单状态
    'status': 'to_be_paid | paid | delivering | delivered | done | apply_return | notify_return | refused | returning | returned',
    'paid_by': 'wx | ali',
    'pay_info': {
        'wx': {},
        'ali': {},
    },

    'products': [
        {
            'product_id': 'PID',  # product_id, 具体购买的产品
            'quantity': 2,  # 购买数量
            'unit_price': 69.9,  # 当时消费单价（商品价格会有变化，账单需要记录购买时的单价. 注意该字段由服务端添加）

            'specifics': {
                # 这当中的信息将做为 POST product_notify_url 请求的 body
                'device_id': 'DEVICE_ID',  # 标识在哪台设备上产生的消费，如为设备服务续期
            },
        },
    ],

    'specifics': {

    },

    'type': 'service|goods',  # 标识该订单服务消费或购买实体商品.目前逻辑已限定不同类型的商品不能混放在一个订单中
    'transfer_enable': True,
    'transfer_enable_time': int(time.time()),
}


class Order(BaseRecord):
    pass


def _init_collection(collection):
    for a in collection.find({'order_no': {'$exists': 0}}):
        while True:
            try:
                order_no = str(generate_random_no(20))
                collection.update_one({'_id': a['_id']}, {'$set': {'order_no': order_no}})
                break
            except Exception as e:
                import logging
                logger = logging.getLogger()
                logger.info('order_no: %d is dup, err msg: %s' % (order_no, str(e)))
    collection.create_index([('order_no', pymongo.ASCENDING)], unique=True)


class OrdersDao(BaseDao):
    periodic_callback = None
    ORDER_NO_LEN = 20
    return_expire_seconds = int(datetime.timedelta(days=7).total_seconds())
    if options.prod:
        transfer_enable_seconds = int(datetime.timedelta(minutes=3).total_seconds())
        # transfer_enable_seconds = int(datetime.timedelta(days=30).total_seconds())
    else:
        transfer_enable_seconds = int(datetime.timedelta(seconds=3).total_seconds())

    def __init__(self, db):
        super(OrdersDao, self).__init__(db, 'orders')
        self.collection.create_index([('order_no', pymongo.ASCENDING)], unique=True)

        if self.__class__.periodic_callback is None:
            # 每分钟检查一次
            # self.__class__.periodic_callback = PeriodicCallback(self._check_orders_status, 1000 * 60)
            self.__class__.periodic_callback = PeriodicCallback(self._check_orders_status, 1000)
            self.__class__.periodic_callback.start()

    @coroutine
    def _check_orders_status(self):
        # 删除12小时内没支付的订单
        filter_cond = {'status': 'to_be_paid', 'created_time': {'$lte': int(time.time()) - 3600 * 12}}
        result = yield self.delete_many(filter_cond)
        if 0 < result.deleted_count:
            self.logger.info('Checker have deleted %d order(s) that not be paid in 12 hours.', result.deleted_count)
        # 收货后超过7天没确认收货,系统自动将状态设置为 done
        # TODO: apply_return | notify_return | refuse | returning | returned 这些状态如何自动切换,需要进一步考虑
        filter_cond = {'status': 'delivered',
                       'delivered_time': {'$lte': int(time.time()) - OrdersDao.return_expire_seconds}}
        modification = {'status': 'done', 'done_time': int(time.time()), 'comment': 'set done by sys'}
        result = yield self.update_many(filter_cond, {'$set': modification})
        if 0 < result.matched_count:
            self.logger.info('Checker have set status of %d order(s) from delivered to done', result.matched_count)
        # 解冻佣金
        filter_cond = {
            'status': 'done',
            'done_time': {
                '$lte': int(time.time()) - OrdersDao.transfer_enable_seconds,
            },
            'transfer_enable': {
                '$exists': 0,
            }
        }
        modification = {
            'transfer_enable': True,
            'transfer_enable_time': int(time.time()),
        }
        result = yield self.update_many(filter_cond, {'$set': modification})
        if 0 < result.matched_count:
            self.logger.info('Checker have set transfer_enable flag of %d order(s)', result.matched_count)

    def _get_record_object(self, data):
        return Order(data)

    @coroutine
    def create_order(self, uid, data):
        data.update(uid=uid, status='to_be_paid', created_time=int(time.time()))
        result = yield self.insert_one_with_unique_no(data, 'order', OrdersDao.ORDER_NO_LEN)
        raise Return(result)

    @coroutine
    def delete_order(self, uid, oid):
        filter_cond = {'_id': ObjectId(oid), 'uid': uid}
        result = yield self.delete_one(filter_cond)
        raise Return(1 == result.deleted_count)

    @coroutine
    def test_delete_order(self, uid, oid):
        filter_cond = {'_id': ObjectId(oid)}
        result = yield self.delete_one(filter_cond)
        raise Return(1 == result.deleted_count)

    @coroutine
    def update_order_no(self, uid, oid):
        while True:
            try:
                order_no = str(generate_random_no(OrdersDao.ORDER_NO_LEN))
                filter_cond = {'_id': ObjectId(oid), 'uid': uid}
                yield self.update_one(filter_cond, {'$set': {'order_no': order_no}})
            except:
                self.logger.info('order_no: %d is dup.' % order_no)
            else:
                raise Return(order_no)

    @coroutine
    def update_order_status(self, oid, data):
        filter_cond = {'_id': ObjectId(oid)}
        order = yield self.find_one(filter_cond, {'status': 1, '_id': 0, 'apply_return_times': 1})
        cur_status = order['status']
        status = data['status']
        specifics = data['specifics'] if 'specifics' in data else None
        if 'to_be_paid' == status:
            if status != cur_status:
                raise Exception('current status(%s) of order is forbidden for the operation' % cur_status)
            data = self.extract_fields(data, required_fields=['deliver_address'], optional_fields=['deliver_remark'])
        elif 'paid' == status:
            if status != cur_status:
                raise Exception('current status(%s) of order is forbidden for the operation' % cur_status)
            data = self.extract_fields(data, required_fields=['deliver_address'], optional_fields=['deliver_remark'])
        elif 'delivering' == status:
            if 'paid' != cur_status and status != cur_status:
                raise Exception('current status(%s) of order is forbidden for the operation' % cur_status)
            data = self.extract_fields(data, required_fields=['deliver_express', 'deliver_express_no'])
            if 'paid' == cur_status:
                data['status'] = status
                data['delivering_time'] = int(time.time())
        elif 'delivered' == status:
            if 'delivering' != cur_status and status != cur_status:
                raise Exception('current status(%s) of order is forbidden for the operation' % cur_status)
            data = self.extract_fields(data, required_fields=['delivered_remark'])
            if 'delivering' == cur_status:
                data['status'] = status
                data['delivered_time'] = int(time.time())
            data = {'status': status}
        elif 'done' == status:
            allowed = [status, 'delivered', 'refuse']
            # if 'delivered' != cur_status and status != cur_status:
            if cur_status not in allowed:
                raise Exception('current status(%s) of order is forbidden for the operation' % cur_status)
            data = self.extract_fields(data, required_fields=['comment'])
            # if 'delivered' == cur_status:
            if status != cur_status:
                data['status'] = status
                data['done_time'] = int(time.time())
        elif 'apply_return' == status:
            allowed = [status, 'delivered', 'refuse']
            # if 'delivered' != cur_status and status != cur_status:
            if cur_status not in allowed:
                raise Exception('current status(%s) of order is forbidden for the operation' % cur_status)
            if 'delivered_time' in order and order['delivered_time'] < (int(time.time()) - self.return_expire_seconds):
                raise Exception('return time expired')
            data = self.extract_fields(data, required_fields=['return_reason'])
            if status != cur_status:
                data['status'] = status
                # 记录申请次数
                if 'apply_return_times' in order:
                    data['apply_return_times'] = order['apply_return_times'] + 1
                else:
                    data['apply_return_times'] = 1
            # 只记录第一次申请退货的时间
            if 'delivered' == cur_status:
                data['apply_return_time'] = int(time.time())
        elif 'notify_return' == status:
            if 'apply_return' != cur_status and status != cur_status:
                raise Exception('current status(%s) of order is forbidden for the operation' % cur_status)
            data = self.extract_fields(data, required_fields=['return_address'], optional_fields=['return_remark'])
            if 'apply_return' == cur_status:
                data['status'] = status
                data['notify_return_time'] = int(time.time())
        elif 'refuse' == status:
            if 'apply_return' != cur_status and status != cur_status:
                raise Exception('current status(%s) of order is forbidden for the operation' % cur_status)
            data = self.extract_fields(data, required_fields=['refuse_reason'])
            if 'apply_return' == cur_status:
                data['status'] = status
                data['refuse_time'] = int(time.time())
        elif 'returning' == status:
            if 'notify_return' != cur_status and status != cur_status:
                raise Exception('current status(%s) of order is forbidden for the operation' % cur_status)
            data = self.extract_fields(data, required_fields=['return_express', 'return_express_no'])
            if 'notify_return' == cur_status:
                data['status'] = status
                data['returning_time'] = int(time.time())
        elif 'returned' == status:
            if 'returning' != cur_status and status != cur_status:
                raise Exception('current status(%s) of order is forbidden for the operation' % cur_status)
            data = self.extract_fields(data, required_fields=['returned_remark'])
            if 'returning' == cur_status:
                data['status'] = status
                data['returned_time'] = int(time.time())
        if specifics is not None:
            data['specifics'] = specifics
        result = yield self.update_one(filter_cond, {'$set': data})
        raise Return(1 == result.matched_count)

    # 支持完成后使用该方法更新订单信息，包括订单状态
    @coroutine
    def pay_update_order(self, oid, data):
        filter_cond = {'_id': ObjectId(oid)}
        result = yield self.update_one(filter_cond, {'$set': data})
        raise Return(1 == result.matched_count)
